<!DOCTYPE html><html class="theme-next pisces use-motion" lang="zh-Hans"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><meta name="theme-color" content="#222"><meta name="baidu-site-verification" content="YdMoGbLGAx"><script src="/lib/pace/pace.min.js?v=1.0.2"></script><link href="/lib/pace/pace-theme-minimal.min.css?v=1.0.2" rel="stylesheet"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><meta name="google-site-verification" content="L2XxDS_YgUmo9ng6ga0rnR1w_IcqBAut-k_te_C1TCQ"><link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css"><link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css"><link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css"><link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.ico?v=5.1.4"><link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.ico?v=5.1.4"><meta name="keywords" content="Java, Spring, Spring boot, Machine learning, Deep learning"><meta name="description" content="Spring AOP 与ApectJ 的目的一致，都是为了统一处理横切业务，但与AspectJ不同的是，Spring AOP 并不尝试提供完整的AOP功能(即使它完全可以实现)，Spring AOP 更注重的是与Spring IOC容器的结合，并结合该优势来解决横切业务的问题，因此在AOP的功能完善方面，相对来说AspectJ具有更大的优势，同时Spring注意到AspectJ在AOP的实现方式上"><meta property="og:type" content="article"><meta property="og:title" content="Spring AOP 你不得不知道的事"><meta property="og:url" content="http://gtwlover.gitee.io/2017/02/27/Spring AOP/index.html"><meta property="og:site_name" content="Better Man"><meta property="og:description" content="Spring AOP 与ApectJ 的目的一致，都是为了统一处理横切业务，但与AspectJ不同的是，Spring AOP 并不尝试提供完整的AOP功能(即使它完全可以实现)，Spring AOP 更注重的是与Spring IOC容器的结合，并结合该优势来解决横切业务的问题，因此在AOP的功能完善方面，相对来说AspectJ具有更大的优势，同时Spring注意到AspectJ在AOP的实现方式上"><meta property="og:image" content="http://gtw.oss-cn-shanghai.aliyuncs.com/Spring/%E7%94%9F%E6%88%90%E4%BB%A3%E7%90%86.png"><meta property="og:updated_time" content="2018-02-12T12:33:46.000Z"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="Spring AOP 你不得不知道的事"><meta name="twitter:description" content="Spring AOP 与ApectJ 的目的一致，都是为了统一处理横切业务，但与AspectJ不同的是，Spring AOP 并不尝试提供完整的AOP功能(即使它完全可以实现)，Spring AOP 更注重的是与Spring IOC容器的结合，并结合该优势来解决横切业务的问题，因此在AOP的功能完善方面，相对来说AspectJ具有更大的优势，同时Spring注意到AspectJ在AOP的实现方式上"><meta name="twitter:image" content="http://gtw.oss-cn-shanghai.aliyuncs.com/Spring/%E7%94%9F%E6%88%90%E4%BB%A3%E7%90%86.png"><script type="text/javascript" id="hexo.configurations">var NexT=window.NexT||{},CONFIG={root:"/",scheme:"Pisces",version:"5.1.4",sidebar:{position:"left",display:"post",offset:12,b2t:!1,scrollpercent:!0,onmobile:!1},fancybox:!0,tabs:!0,motion:{enable:!0,async:!1,transition:{post_block:"fadeIn",post_header:"slideDownIn",post_body:"slideDownIn",coll_header:"slideLeftIn",sidebar:"slideUpIn"}},duoshuo:{userId:"gtwlover",author:"博主"},algolia:{applicationID:"GOSX1252HP",apiKey:"eefb124f52fc415c356384bb0ed50bda",indexName:"myblog",hits:{per_page:10},labels:{input_placeholder:"请输入要查找的关键字",hits_empty:"未找到 ${query} 相关结果",hits_stats:"${hits} 条相关条目，使用了 ${time} 毫秒"}}}</script><link rel="canonical" href="http://gtwlover.gitee.io/2017/02/27/Spring AOP/"><title>Spring AOP 你不得不知道的事 | Better Man</title><script>!function(e,a,t,n,g,c,o){e.GoogleAnalyticsObject=g,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,c=a.createElement(t),o=a.getElementsByTagName(t)[0],c.async=1,c.src="https://www.google-analytics.com/analytics.js",o.parentNode.insertBefore(c,o)}(window,document,"script",0,"ga"),ga("create","UA-97111795-1","auto"),ga("send","pageview")</script></head><body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans"><div class="container sidebar-position-left page-post-detail"><div class="headband"></div> <a href="https://github.com/gaotingwang"><img style="position:absolute;top:0;right:0;border:0" src="https://camo.githubusercontent.com/652c5b9acfaddf3a9c326fa6bde407b87f7be0f4/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6f72616e67655f6666373630302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png"></a><header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="header-inner"><div class="site-brand-wrapper"><div class="site-meta"><div class="custom-logo-site-title"><a href="/" class="brand" rel="start"><span class="logo-line-before"><i></i></span> <span class="site-title">Better Man</span><span class="logo-line-after"><i></i></span></a></div><p class="site-subtitle">时间以北慕城南，故人以北爱荒凉</p></div><div class="site-nav-toggle"> <button><span class="btn-bar"></span><span class="btn-bar"></span><span class="btn-bar"></span></button></div></div><nav class="site-nav"><ul id="menu" class="menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="menu-item-icon fa fa-fw fa-home"></i><br> 首页</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i><br> 归档</a></li><li class="menu-item menu-item-search"><a href="javascript:;" class="popup-trigger"><i class="menu-item-icon fa fa-search fa-fw"></i><br> 搜索</a></li></ul><div class="site-search"><div class="algolia-popup popup search-popup"><div class="algolia-search"><div class="algolia-search-input-icon"><i class="fa fa-search"></i></div><div class="algolia-search-input" id="algolia-search-input"></div></div><div class="algolia-results"><div id="algolia-stats"></div><div id="algolia-hits"></div><div id="algolia-pagination" class="algolia-pagination"></div></div><span class="popup-btn-close"><i class="fa fa-times-circle"></i></span></div></div></nav></div></header><main id="main" class="main"><div class="main-inner"><div class="content-wrap"><div id="content" class="content"><div id="posts" class="posts-expand"><article class="post post-type-normal" itemscope itemtype="http://schema.org/Article"><div class="post-block"><link itemprop="mainEntityOfPage" href="http://gtwlover.gitee.io/2017/02/27/Spring AOP/"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="name" content="脱毛大白兔"><meta itemprop="description" content=""><meta itemprop="image" content="http://gtw.oss-cn-shanghai.aliyuncs.com/myself/my3.jpeg?x-oss-process=style/zoom"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="Better Man"></span><header class="post-header"><h1 class="post-title" itemprop="name headline">Spring AOP 你不得不知道的事</h1><div class="post-meta"><span class="post-time"><span class="post-meta-item-icon"><i class="fa fa-calendar-o"></i></span> <span class="post-meta-item-text">发表于</span> <time title="创建于" itemprop="dateCreated datePublished" datetime="2017-02-27T21:08:29+08:00">2017-02-27</time></span> <span class="post-category"><span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-folder-o"></i></span> <span class="post-meta-item-text">分类于</span> <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/Spring/" itemprop="url" rel="index"><span itemprop="name">Spring</span></a></span></span> <span class="post-comments-count"><span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-comment-o"></i></span><a href="/2017/02/27/Spring AOP/#comments" itemprop="discussionUrl"><span class="post-comments-count gitment-comments-count" data-xid="/2017/02/27/Spring AOP/" itemprop="commentsCount"></span></a></span> <span id="/2017/02/27/Spring AOP/" class="leancloud_visitors" data-flag-title="Spring AOP 你不得不知道的事"><span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-eye"></i></span> <span class="post-meta-item-text">阅读次数&#58;</span><span class="leancloud-visitors-count"></span></span><div class="post-description"> Spring AOP 与ApectJ 的目的一致，都是为了统一处理横切业务，但与AspectJ不同的是，Spring AOP 并不尝试提供完整的AOP功能(即使它完全可以实现)，Spring AOP 更注重的是与Spring IOC容器的结合，并结合该优势来解决横切业务的问题，因此在AOP的功能完善方面，相对来说AspectJ具有更大的优势，同时Spring注意到AspectJ在AOP的实现方式上依赖于特殊编译器(ajc编译器)，因此Spring很机智回避了这点，转向采用动态代理技术的实现原理来构建Spring AOP的内部机制（动态织入），这是与AspectJ（静态织入）最根本的区别。</div></div></header><div class="post-body" itemprop="articleBody"><p><code>Spring AOP</code> 与<code>ApectJ</code> 的目的一致，都是为了统一处理横切业务，但与<code>AspectJ</code>不同的是，<code>Spring AOP</code> 并不尝试提供完整的<code>AOP</code>功能(即使它完全可以实现)，<code>Spring AOP</code> 更注重的是与<code>Spring IOC</code>容器的结合，并结合该优势来解决横切业务的问题，因此在<code>AOP</code>的功能完善方面，相对来说<code>AspectJ</code>具有更大的优势，同时<code>Spring</code>注意到<code>AspectJ</code>在<code>AOP</code>的实现方式上依赖于特殊编译器(ajc编译器)，因此<code>Spring</code>很机智回避了这点，转向采用动态代理技术的实现原理来构建<code>Spring AOP</code>的内部机制（动态织入），这是与<code>AspectJ</code>（静态织入）最根本的区别。在<code>AspectJ</code> 1.5后，引入<code>@Aspect</code>形式的注解风格的开发，Spring也非常快地跟进了这种方式，因此<code>Spring 2.0</code>后便使用了与<code>AspectJ</code>一样的注解。请注意，<code>Spring</code> 只是使用了与 <code>AspectJ 5</code> 一样的注解，但仍然没有使用 <code>AspectJ</code>的编译器，底层依是动态代理技术的实现，因此并不依赖于 <code>AspectJ</code> 的编译器。</p><h1 id="Spring-AOP的实现原理概要"><a href="#Spring-AOP的实现原理概要" class="headerlink" title="Spring AOP的实现原理概要"></a>Spring AOP的实现原理概要</h1><p><code>Spring AOP</code>的实现原理是基于动态织入的动态代理技术，而<code>AspectJ</code>则是静态织入，而动态代理技术又分为<code>Java JDK</code>动态代理和<code>CGLIB</code>动态代理，前者是基于反射技术的实现，后者是基于继承的机制实现，下面通过一个简单的例子来分析这两种技术的代码实现。</p><h2 id="JDK动态代理"><a href="#JDK动态代理" class="headerlink" title="JDK动态代理"></a>JDK动态代理</h2><p>先看一个简单的例子，声明一个A类并实现ExInterface接口，利用JDK动态代理技术在execute()执行前后织入权限验证和日志记录，注意这里仅是演示代码并不代表实际应用。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 自定义的接口类，JDK动态代理的实现必须有对应的接口类</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Person</span> </span>&#123;</div><div class="line">    <span class="function">String <span class="title">sayHello</span><span class="params">(String name)</span></span>;</div><div class="line">    <span class="function"><span class="keyword">void</span> <span class="title">eat</span><span class="params">(String food)</span></span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Chinese</span> <span class="keyword">implements</span> <span class="title">Person</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">sayHello</span><span class="params">(String name)</span> </span>&#123;</div><div class="line">        System.out.println(<span class="string">"-- 正在执行 sayHello 方法 --"</span>);</div><div class="line">        <span class="keyword">return</span> name;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">(String food)</span> </span>&#123;</div><div class="line">        System.out.println(<span class="string">"我正在吃 :"</span>+ food);</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * JDK的动态代理是基于接口的</div><div class="line"> * 每一个动态代理类都必须要实现InvocationHandler这个接口，并且每个代理类的实例都关联到了一个handler</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JDKDynamicProxy</span> <span class="keyword">implements</span> <span class="title">InvocationHandler</span> </span>&#123;</div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 这个就是我们要代理的真实对象</div><div class="line">     */</div><div class="line">    <span class="keyword">private</span> Object subject;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 创建代理对象</div><div class="line">     * <span class="doctag">@param</span> target 被代理的对象</div><div class="line">     * <span class="doctag">@return</span> 代理对象</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">newProxyInstance</span><span class="params">(Object target)</span></span>&#123;</div><div class="line">        <span class="keyword">this</span>.subject = target;</div><div class="line">		<span class="comment">/*</span></div><div class="line">		 * 通过Proxy的newProxyInstance方法来创建我们的代理对象，看看其三个参数</div><div class="line"> 		 * 第一个参数handler.getClass().getClassLoader()，这里使用handler这个类的ClassLoader对象来加载我们的代理对象</div><div class="line">		 * 第二个参数realSubject.getClass().getInterfaces()，这里为代理对象提供的接口是真实对象所实行的接口，表示我要代理的是该真实对象，这样就能调用这组接口中的方法了</div><div class="line">		 * 第三个参数handler，我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上</div><div class="line">		 *</div><div class="line">		 * Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象，它并不是我们的InvocationHandler类型</div><div class="line">		 * 也不是我们定义的那组接口的类型，而是在运行是动态生成的一个对象，并且命名方式都是这样的形式，以$开头，proxy为中，最后一个数字表示对象的标号。</div><div class="line">		 */</div><div class="line">        <span class="keyword">return</span> Proxy.newProxyInstance(<span class="keyword">this</span>.subject.getClass().getClassLoader(),</div><div class="line">                <span class="comment">// 传递的是实现类所实现的接口 targetObject.getClass().getInterfaces()，所以JDK只能对于接口进行做代理</span></div><div class="line">                <span class="keyword">this</span>.subject.getClass().getInterfaces(),</div><div class="line">                <span class="keyword">this</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 当我们通过代理对象调用一个方法的时候，这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来进行调用。</div><div class="line">     * <span class="doctag">@param</span> proxy 生成的代理对象</div><div class="line">     * <span class="doctag">@param</span> method 指代的是我们所要调用真实对象的某个方法的Method对象</div><div class="line">     * <span class="doctag">@param</span> args 指代的是调用真实对象某个方法时接受的参数</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable </span>&#123;</div><div class="line">        <span class="comment">// 在代理真实对象前我们可以添加一些自己的操作</span></div><div class="line">        System.out.println(<span class="string">"前置代理"</span>);</div><div class="line">        System.out.println(<span class="string">"Method:"</span> + method);</div><div class="line">        <span class="comment">// 当代理对象调用真实对象的方法时，其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用</span></div><div class="line">        Object obj = method.invoke(subject, args);</div><div class="line">        <span class="comment">// 在代理真实对象后我们也可以添加一些自己的操作</span></div><div class="line">        System.out.println(<span class="string">"后置代理"</span>);</div><div class="line">        <span class="keyword">return</span> obj;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"> <span class="comment">//测试验证</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProxyMain</span> </span>&#123;</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</div><div class="line">        <span class="comment">// 基于jdk的动态代理</span></div><div class="line">        JDKDynamicProxy handler = <span class="keyword">new</span> JDKDynamicProxy();</div><div class="line">        Person proxyChinese = (Person) handler.newProxyInstance(<span class="keyword">new</span> Chinese());</div><div class="line">        System.out.println(<span class="string">"JDK动态代理："</span> + proxyChinese.getClass().getName());</div><div class="line">        <span class="comment">// 通过代理对象来调用方法的时候，起实际就是委托由其关联到的 handler 对象的invoke方法中来调用，并不是自己来真实调用，而是通过代理的方式来调用的。</span></div><div class="line">        proxyChinese.sayHello(<span class="string">"李四"</span>);</div><div class="line">        System.out.println();</div><div class="line">        proxyChinese.eat(<span class="string">"香蕉"</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>运行结果：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">JDK动态代理：com.sun.proxy.$Proxy0</div><div class="line">前置代理</div><div class="line">Method:public abstract java.lang.String com.gtw.util.prox.model.Person.sayHello(java.lang.String)</div><div class="line">-- 正在执行 sayHello 方法 --</div><div class="line">后置代理</div><div class="line"></div><div class="line">前置代理</div><div class="line">Method:public abstract void com.gtw.util.prox.model.Person.eat(java.lang.String)</div><div class="line">我正在吃 :香蕉</div><div class="line">后置代理</div></pre></td></tr></table></figure><p>在Chinese的sayhello和eat方法里面并没有调用任何其他的代码，也没有直接操作person对象，相反地只是调用了proxy代理对象的方法，最终结果却是预期的，这就是动态代理技术。实际上动态代理的底层是通过反射技术来实现，只要拿到Chinese类的class文件和Chinese类的实现接口，很自然就可以生成相同接口的代理类并调用person对象的方法了，关于底层反射技术的实现，暂且不过多讨论，请注意实现java的动态代理是有先决条件的，该条件是目标对象必须带接口，如Chinese类的接口是Person，通过Person接口动态代理技术便可以创建与Chinese类类型相同的代理对象，如下代码演示了创建并调用时利用多态生成的proxy对象：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">JDKDynamicProxy handler = <span class="keyword">new</span> JDKDynamicProxy();</div><div class="line"></div><div class="line"> <span class="comment">//创建JDK代理类</span></div><div class="line">Person proxyChinese = (Person) handler.newProxyInstance(<span class="keyword">new</span> Chinese());</div><div class="line">System.out.println(<span class="string">"JDK动态代理："</span> + proxyChinese.getClass().getName());</div><div class="line"></div><div class="line"><span class="comment">// 通过代理对象来调用方法的时候，起实际就是委托由其关联到的 handler 对象的invoke方法中来调用，并不是自己来真实调用，而是通过代理的方式来调用的。</span></div><div class="line">proxyChinese.sayHello(<span class="string">"李四"</span>);</div><div class="line">proxyChinese.eat(<span class="string">"香蕉"</span>);</div></pre></td></tr></table></figure><p>代理对象的创建是通过<code>Proxy</code>类达到的，<code>Proxy</code>类由Java JDK提供，利用<code>Proxy.newProxyInstance</code>方法便可以动态生成代理对象(proxyChinese)，底层通过反射实现的，该方法需要3个参数</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line">* <span class="doctag">@param</span> loader 类加载器，一般传递目标对象(A类即被代理的对象)的类加载器</div><div class="line">* <span class="doctag">@param</span> interfaces 目标对象(A)的实现接口</div><div class="line">* <span class="doctag">@param</span> h 回调处理句柄(后面会分析到)</div><div class="line">*/</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title">newProxyInstance</span><span class="params">(ClassLoader loader,Class&lt;?&gt;[] interfaces,InvocationHandler h)</span></span></div></pre></td></tr></table></figure><p>到此并没结束，因为有接口还是远远不够，代理类还需要实现<code>InvocationHandler</code>接口，也是由JDK提供，代理类必须实现的并重写<code>invoke</code>方法，完全可以把<code>InvocationHandler</code>看成一个回调函数(Callback)，Proxy方法创建代理对象proxy后，当调用sayHello方法(代理对象也实现Person)时,将会回调<code>InvocationHandler.invoke</code>方法，因此我们可以在<code>invoke</code>方法中来控制被代理对象(目标对象)的方法执行，从而在该方法前后动态增加其他需要执行的业务：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable </span>&#123;</div><div class="line">    <span class="comment">// 在代理真实对象前我们可以添加一些自己的操作</span></div><div class="line">    System.out.println(<span class="string">"前置代理"</span>);</div><div class="line">    System.out.println(<span class="string">"Method:"</span> + method);</div><div class="line">    <span class="comment">// 当代理对象调用真实对象的方法时，其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用</span></div><div class="line">    Object obj = method.invoke(subject, args);</div><div class="line">    <span class="comment">// 在代理真实对象后我们也可以添加一些自己的操作</span></div><div class="line">    System.out.println(<span class="string">"后置代理"</span>);</div><div class="line">    <span class="keyword">return</span> obj;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>invoke方法有三个参数：</p><ul><li>Object proxy ：生成的代理对象</li><li>Method method：目标对象的方法，通过反射调用</li><li>Object[] args：目标对象方法的参数</li></ul><p>这就是Java JDK动态代理的代码实现过程，运用JDK动态代理，被代理类(目标对象，如Chinese类)，必须已有实现接口如(Person)，因为JDK提供的Proxy类将通过目标对象的类加载器<code>ClassLoader</code>和<code>Interface</code>，以及句柄(Callback)创建与Chinese类拥有相同接口的代理对象proxy，该代理对象将拥有接口Person中的所有方法，同时代理类必须实现一个类似回调函数的<code>InvocationHandler</code>接口并重写该接口中的<code>invoke</code>方法，当调用proxy的每个方法时，<code>invoke</code>方法将被调用，利用该特性，可以在<code>invoke</code>方法中对目标对象(被代理对象如Chinese)方法执行的前后动态添加其他外围业务操作，此时无需触及目标对象的任何代码，也就实现了外围业务的操作与目标对象(被代理对象如Chinese)完全解耦合的目的。当然缺点也很明显需要拥有接口，这也就有了后来的CGLIB动态代理了。</p><h2 id="CGLIB动态代理"><a href="#CGLIB动态代理" class="headerlink" title="CGLIB动态代理"></a>CGLIB动态代理</h2><p>通过CGLIB动态代理实现上述功能并不要求目标对象拥有接口类，实际上CGLIB动态代理是通过继承的方式实现的，因此可以减少没必要的接口，下面直接通过简单案例协助理解（CGLIB是一个开源项目，github网址是：<a href="https://github.com/cglib/cglib" target="_blank" rel="external">https://github.com/cglib/cglib</a>）。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//被代理的类即目标对象</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">American</span> </span>&#123;</div><div class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">sayHello</span><span class="params">(String name)</span> </span>&#123;</div><div class="line">        System.out.println(<span class="string">"-- execute sayHello method --"</span>);</div><div class="line">        <span class="keyword">return</span> name;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">(String food)</span> </span>&#123;</div><div class="line">        System.out.println(<span class="string">"I'm eating "</span>+ food);</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">//代理类</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CglibDynamicProxy</span> <span class="keyword">implements</span> <span class="title">MethodInterceptor</span> </span>&#123;</div><div class="line">    <span class="keyword">private</span> Object target;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 创建代理对象</div><div class="line">     * <span class="doctag">@param</span> target 被代理的对象</div><div class="line">     * <span class="doctag">@return</span> 代理对象</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">getProxyInstance</span><span class="params">(Object target)</span></span>&#123;</div><div class="line">        <span class="keyword">this</span>.target = target;</div><div class="line">        <span class="comment">// 声明增强类实例</span></div><div class="line">        Enhancer enhancer = <span class="keyword">new</span> Enhancer();</div><div class="line">        <span class="comment">// 设置被代理类字节码，CGLIB根据字节码生成被代理类的子类</span></div><div class="line">        enhancer.setSuperclass(<span class="keyword">this</span>.target.getClass());</div><div class="line">        <span class="comment">// 设置要代理的拦截器，回调函数，即一个方法拦截 new MethodInterceptor()</span></div><div class="line">        enhancer.setCallback(<span class="keyword">this</span>);</div><div class="line">        <span class="comment">// 创建代理对象 实例</span></div><div class="line">        <span class="keyword">return</span> enhancer.create();</div><div class="line">    &#125;</div><div class="line"></div><div class="line">	<span class="comment">/**</span></div><div class="line">     * 回调函数</div><div class="line">     * <span class="doctag">@param</span> obj 代理对象</div><div class="line">     * <span class="doctag">@param</span> method 委托类方法</div><div class="line">     * <span class="doctag">@param</span> args 方法参数</div><div class="line">     * <span class="doctag">@param</span> proxy 每个被代理的方法都对应一个MethodProxy对象，methodProxy.invokeSuper方法最终调用委托类(目标类)的原始方法</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">intercept</span><span class="params">(Object obj, Method method, Object[] args,</span></span></div><div class="line">                            MethodProxy proxy) <span class="keyword">throws</span> Throwable &#123;</div><div class="line">        <span class="comment">// 在代理真实对象操作前 我们可以添加一些自己的操作</span></div><div class="line">        System.out.println(<span class="string">"前置代理,增强处理"</span>);</div><div class="line"></div><div class="line">        proxy.invokeSuper(obj, args);</div><div class="line"></div><div class="line">        <span class="comment">// 在代理真实对象操作后 我们也可以添加一些自己的操作</span></div><div class="line">        System.out.println(<span class="string">"后置代理,增强处理"</span>);</div><div class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">//测试</span></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</div><div class="line">    <span class="comment">// 基于cglib的动态代理</span></div><div class="line">    CglibDynamicProxy cglib = <span class="keyword">new</span> CglibDynamicProxy();</div><div class="line">    American proxyAmerican = (American) cglib.getProxyInstance(<span class="keyword">new</span> American());</div><div class="line">    System.out.println(<span class="string">"CGLIB动态代理："</span> + proxyAmerican.getClass().getName());</div><div class="line">    <span class="comment">// proxyAmerican 是通过Enhancer动态生成的American对象</span></div><div class="line">    proxyAmerican.sayHello(<span class="string">"Tom"</span>);</div><div class="line">    System.out.println();</div><div class="line">    proxyAmerican.eat(<span class="string">"apple"</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>运行结果：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">CGLIB动态代理：com.gtw.util.prox.model.American$$EnhancerByCGLIB$$dcaef7b3</div><div class="line">前置代理,增强处理</div><div class="line">-- execute sayHello method --</div><div class="line">后置代理,增强处理</div><div class="line"></div><div class="line">前置代理,增强处理</div><div class="line">I&apos;m eating apple</div><div class="line">后置代理,增强处理</div></pre></td></tr></table></figure><p>从代码看被代理的类无需接口即可实现动态代理，而<code>CGLibProxy</code>代理类需要实现一个方法拦截器接口<code>MethodInterceptor</code>并重写<code>intercept</code>方法，类似<code>JDK</code>动态代理的<code>InvocationHandler</code>接口，也是理解为回调函数，同理每次调用代理对象的方法时，<code>intercept</code>方法都会被调用，利用该方法便可以在运行时对方法执行前后进行动态增强。关于代理对象创建则通过<code>Enhancer</code>类来设置的，<code>Enhancer</code>是一个用于产生代理对象的类，作用类似JDK的Proxy类，因为<code>CGLib</code>底层是通过继承实现的动态代理，因此需要传递目标对象(American)的Class，同时需要设置一个回调函数对调用方法进行拦截并进行相应处理，最后通过<code>enhancer.create()</code>创建目标对象(American)的代理对象。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 创建代理对象</div><div class="line"> * <span class="doctag">@param</span> target 被代理的对象</div><div class="line"> * <span class="doctag">@return</span> 代理对象</div><div class="line"> */</div><div class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">getProxyInstance</span><span class="params">(Object target)</span></span>&#123;</div><div class="line">    <span class="keyword">this</span>.target = target;</div><div class="line">    <span class="comment">// 声明增强类实例</span></div><div class="line">    Enhancer enhancer = <span class="keyword">new</span> Enhancer();</div><div class="line">    <span class="comment">// 设置被代理类字节码，CGLIB根据字节码生成被代理类的子类</span></div><div class="line">    enhancer.setSuperclass(<span class="keyword">this</span>.target.getClass());</div><div class="line">    <span class="comment">// 设置要代理的拦截器，回调函数，即一个方法拦截 new MethodInterceptor()</span></div><div class="line">    enhancer.setCallback(<span class="keyword">this</span>);</div><div class="line">    <span class="comment">// 创建代理对象 实例</span></div><div class="line">    <span class="keyword">return</span> enhancer.create();</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>关于JDK代理技术 和 CGLIB代理技术到这就讲完了，我们也应该明白 Spring AOP 确实是通过 CGLIB或者JDK代理 来动态地生成代理对象，这个代理对象指的就是 AOP 代理类，而 AOP 代理类的方法则通过在目标对象的切入点动态地织入增强处理，从而完成了对目标方法的增强。这里并没有非常深入去分析这两种技术，更倾向于向大家演示Spring AOP 底层实现的最简化的模型代码，Spring AOP内部已都实现了这两种技术，Spring AOP 在使用时机上也进行自动化调整，当有接口时会自动选择JDK动态代理技术，如果没有则选择CGLIB技术，当然Spring AOP的底层实现并没有这么简单，为更简便生成代理对象，Spring AOP 内部实现了一个专注于生成代理对象的工厂类，这样就避免了大量的手动编码，这点也是十分人性化的，但最核心的还是动态代理技术。从性能上来说，Spring AOP 虽然无需特殊编译器协助，但性能上并不优于AspectJ的静态织入，这点了解一下即可。最后给出Spring AOP简化的原理模型如下图：</p><p><img src="http://gtw.oss-cn-shanghai.aliyuncs.com/Spring/%E7%94%9F%E6%88%90%E4%BB%A3%E7%90%86.png" alt="img"></p><h1 id="基于Aspect-Spring-AOP-开发"><a href="#基于Aspect-Spring-AOP-开发" class="headerlink" title="基于Aspect Spring AOP 开发"></a>基于Aspect Spring AOP 开发</h1><h2 id="简单案例快速入门"><a href="#简单案例快速入门" class="headerlink" title="简单案例快速入门"></a>简单案例快速入门</h2><p>使用Spring 2.0引入的注解方式，编写Spring AOP的Aspect 类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Aspect</span></div><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyAspect</span> </span>&#123;</div><div class="line">  </div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 定义一个切点Pointcut，在指定的地方加入切面</div><div class="line">     * Pointcut定义了“何处”，切点有助于缩小切面所通知的连接点范围</div><div class="line">     */</div><div class="line">    <span class="meta">@Pointcut</span>(<span class="string">"@within(com.gtw.util.transactional.annotation.Transactional) || @annotation(com.gtw.util.transactional.annotation.Transactional)"</span>)</div><div class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">point</span><span class="params">()</span> </span>&#123; &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 前置通知</div><div class="line">     */</div><div class="line">    <span class="meta">@Before</span>(<span class="string">"point()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">before</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"前置通知...."</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 后置通知</div><div class="line">     * returnVal,切点方法执行后的返回值</div><div class="line">     */</div><div class="line">    <span class="meta">@AfterReturning</span>(value=<span class="string">"point()"</span>,returning = <span class="string">"returnVal"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">AfterReturning</span><span class="params">(Object returnVal)</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"后置通知...."</span>+returnVal);</div><div class="line">    &#125;</div><div class="line"></div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 环绕通知</div><div class="line">     * <span class="doctag">@param</span> joinPoint 可用于执行切点的类</div><div class="line">     * <span class="doctag">@return</span></div><div class="line">     * <span class="doctag">@throws</span> Throwable</div><div class="line">     */</div><div class="line">    <span class="meta">@Around</span>(<span class="string">"point()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">around</span><span class="params">(ProceedingJoinPoint joinPoint)</span> <span class="keyword">throws</span> Throwable </span>&#123;</div><div class="line">        System.out.println(<span class="string">"环绕通知前...."</span>);</div><div class="line">        Object obj= (Object) joinPoint.proceed();</div><div class="line">        System.out.println(<span class="string">"环绕通知后...."</span>);</div><div class="line">        <span class="keyword">return</span> obj;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 抛出通知</div><div class="line">     * <span class="doctag">@param</span> e</div><div class="line">     */</div><div class="line">    <span class="meta">@AfterThrowing</span>(value=<span class="string">"point()"</span>,throwing = <span class="string">"e"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">afterThrowable</span><span class="params">(Throwable e)</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"出现异常:msg="</span>+e.getMessage());</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 无论什么情况下都会执行的方法</div><div class="line">     */</div><div class="line">    <span class="meta">@After</span>(value=<span class="string">"point()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">after</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"最终通知...."</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>具体调用：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Service</span></div><div class="line"><span class="meta">@Qualifier</span>(<span class="string">"userService"</span>)</div><div class="line"><span class="meta">@Transactional</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title">IUserService</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">addUser</span><span class="params">()</span> </span>&#123;</div><div class="line">        System.out.println(<span class="string">"添加用户"</span>);</div><div class="line">        <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="meta">@Transactional</span>(rollbackForClassName = <span class="string">"MyException"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">updateUser</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</div><div class="line">        System.out.println(<span class="string">"修改用户"</span>);</div><div class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> MyException(<span class="string">"检查型异常"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">deleteUser</span><span class="params">()</span> </span>&#123;</div><div class="line">        System.out.println(<span class="string">"删除用户"</span>);</div><div class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"运行时异常"</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>利用Spring引入的Aspect注解开发功能定义Aspect类即MyAspect，在该aspect类中，编写了5种注解类型的通知函数，分别是前置通知<code>@Before</code>、后置通知<code>@AfterReturning</code>、环绕通知<code>@Around</code>、异常通知<code>@AfterThrowing</code>、最终通知<code>@After</code>，这5种通知与AspectJ的通知类型几乎是一样的，并注解通知上使用execution关键字定义的切点表达式，即指明该通知要应用的目标函数，当只有一个execution参数时，value属性可以省略，当含两个以上的参数，value必须注明，如存在返回值时。当然除了把切点表达式直接传递给通知注解类型外，还可以使用<code>@Pointcut</code>来定义切点匹配表达式，这个与AspectJ使用关键字pointcut是一样的，后面分析。目标类和aspect类定义完成后，最后所有类的创建都交由SpringIOC容器处理。</p><h2 id="再谈Spring-AOP-术语"><a href="#再谈Spring-AOP-术语" class="headerlink" title="再谈Spring AOP 术语"></a>再谈Spring AOP 术语</h2><p>Spring AOP 的实现是遵循AOP规范的，特别是以AspectJ（与java无缝整合）为参考，因此在AOP的术语概念上与AspectJ的AOP术语是一样的，如切点(pointcut)定义需要应用通知的目标函数，通知则是那些需要应用到目标函数而编写的函数体，切面(Aspect)则是通知与切点的结合。织入(weaving)，将aspect类应用到目标函数(类)的过程，只不过Spring AOP底层是通过动态代理技术实现罢了。</p><h1 id="基于注解的Spring-AOP开发"><a href="#基于注解的Spring-AOP开发" class="headerlink" title="基于注解的Spring AOP开发"></a>基于注解的Spring AOP开发</h1><h2 id="定义切入点函数"><a href="#定义切入点函数" class="headerlink" title="定义切入点函数"></a>定义切入点函数</h2><p>在案例中，定义过滤切入点函数时，是直接把execution已定义匹配表达式作为值传递给通知类型的如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@After</span>(value=<span class="string">"execution(* com.gtw.util.transactional.service.addUser(..))"</span>)</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">after</span><span class="params">()</span></span>&#123;</div><div class="line">	System.out.println(<span class="string">"最终通知...."</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>除了上述方式外，还可采用与ApectJ中使用<code>Pointcut</code>关键字类似的方式定义切入点表达式如下，使用<code>@Pointcut</code>注解：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 使用Pointcut定义切点</div><div class="line"> */</div><div class="line"><span class="meta">@Pointcut</span>(<span class="string">"execution(* com.gtw.util.transactional.service.addUser(..))"</span>)</div><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">myPointcut</span><span class="params">()</span></span>&#123;&#125;</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * 应用切入点函数</div><div class="line"> */</div><div class="line"><span class="meta">@After</span>(value=<span class="string">"myPointcut()"</span>)</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">afterDemo</span><span class="params">()</span></span>&#123;</div><div class="line">    System.out.println(<span class="string">"最终通知...."</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>使用@Pointcut注解进行定义，应用到通知函数afterDemo()时直接传递切点表达式的函数名称myPointcut()即可，比较简单，下面接着介绍切点指示符。</p><h2 id="切入点指示符"><a href="#切入点指示符" class="headerlink" title="切入点指示符"></a>切入点指示符</h2><p>为了方法通知应用到相应过滤的目标方法上，SpringAOP提供了匹配表达式，这些表达式也叫切入点指示符，在前面的案例中，它们已多次出现。</p><p>表达式中使用”execution“就可以满足大部分的要求。表达式格式如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)</div></pre></td></tr></table></figure><ul><li>modifiers-pattern：方法的操作权限</li><li><strong>ret-type-pattern</strong>：返回值</li><li>declaring-type-pattern：方法所在的包</li><li><strong>name-pattern</strong>：方法名</li><li>parm-pattern：参数名</li><li>throws-pattern：异常</li></ul><p>其中，除ret-type-pattern和name-pattern之外，其他都是可选的。</p><h3 id="通配符"><a href="#通配符" class="headerlink" title="通配符"></a>通配符</h3><p>在定义匹配表达式时，通配符几乎随处可见，如*、.. 、+ ，它们的含义如下：</p><ul><li><p>.. ：匹配方法定义中的任意数量的参数，此外还匹配类定义中的任意数量包</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">//任意返回值，任意名称，任意参数的公共方法</div><div class="line">execution(public * *(..))</div><div class="line">//匹配com.gtw.dao包及其子包中所有类中的所有方法</div><div class="line">within(com.gtw.dao..*)</div></pre></td></tr></table></figure></li><li><p>＋ ：匹配给定类的任意子类</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">//匹配实现了DaoUser接口的所有子类的方法</div><div class="line">within(com.gtw.dao.DaoUser+)</div></pre></td></tr></table></figure></li><li><p>＊ ：匹配任意数量的字符</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">//匹配com.gtw.service包及其子包中所有类的所有方法</div><div class="line">within(com.gtw.service..*)</div><div class="line">//匹配以set开头，参数为int类型，任意返回值的方法</div><div class="line">execution(* set*(int))</div></pre></td></tr></table></figure></li></ul><h3 id="类型签名表达式"><a href="#类型签名表达式" class="headerlink" title="类型签名表达式"></a>类型签名表达式</h3><p>为了方便类型（如接口、类名、包名）过滤方法，Spring AOP 提供了within关键字。其语法格式如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">within(&lt;type name&gt;)</div></pre></td></tr></table></figure><p>type name 则使用包名或者类名替换即可，来点案例吧。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//匹配com.gtw.dao包及其子包中所有类中的所有方法</span></div><div class="line"><span class="meta">@Pointcut</span>(<span class="string">"within(com.gtw.dao..*)"</span>)</div><div class="line"></div><div class="line"><span class="comment">//匹配UserDaoImpl类中所有方法</span></div><div class="line"><span class="meta">@Pointcut</span>(<span class="string">"within(com.gtw.dao.UserDaoImpl)"</span>)</div><div class="line"></div><div class="line"><span class="comment">//匹配UserDaoImpl类及其子类中所有方法</span></div><div class="line"><span class="meta">@Pointcut</span>(<span class="string">"within(com.gtw.dao.UserDaoImpl+)"</span>)</div><div class="line"></div><div class="line"><span class="comment">//匹配所有实现UserDao接口的类的所有方法</span></div><div class="line"><span class="meta">@Pointcut</span>(<span class="string">"within(com.gtw.dao.UserDao+)"</span>)<span class="number">1234567891011</span></div></pre></td></tr></table></figure><h3 id="方法签名表达式"><a href="#方法签名表达式" class="headerlink" title="方法签名表达式"></a>方法签名表达式</h3><p>如果想根据方法签名进行过滤，关键字execution可以帮到我们，语法表达式如下</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">//scope ：方法作用域，如public,private,protect</div><div class="line">//returnt-type：方法返回值类型</div><div class="line">//fully-qualified-class-name：方法所在类的完全限定名称</div><div class="line">//parameters 方法参数</div><div class="line">execution(&lt;scope&gt; &lt;return-type&gt; &lt;fully-qualified-class-name&gt;.*(parameters))</div></pre></td></tr></table></figure><p>对于给定的作用域、返回值类型、完全限定类名以及参数匹配的方法将会应用切点函数指定的通知，这里给出模型案例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//匹配UserDaoImpl类中的所有方法</span></div><div class="line"><span class="meta">@Pointcut</span>(<span class="string">"execution(* com.gtw.dao.UserDaoImpl.*(..))"</span>)</div><div class="line"></div><div class="line"><span class="comment">//匹配UserDaoImpl类中的所有公共的方法</span></div><div class="line"><span class="meta">@Pointcut</span>(<span class="string">"execution(public * com.gtw.dao.UserDaoImpl.*(..))"</span>)</div><div class="line"></div><div class="line"><span class="comment">//匹配UserDaoImpl类中的所有公共方法并且返回值为int类型</span></div><div class="line"><span class="meta">@Pointcut</span>(<span class="string">"execution(public int com.gtw.dao.UserDaoImpl.*(..))"</span>)</div><div class="line"></div><div class="line"><span class="comment">//匹配UserDaoImpl类中第一个参数为int类型的所有公共的方法</span></div><div class="line"><span class="meta">@Pointcut</span>(<span class="string">"execution(public * com.gtw.dao.UserDaoImpl.*(int , ..))"</span>)</div></pre></td></tr></table></figure><h3 id="其他指示符"><a href="#其他指示符" class="headerlink" title="其他指示符"></a>其他指示符</h3><ul><li><p>bean：Spring AOP扩展的，AspectJ没有对于指示符，用于匹配特定名称的Bean对象的执行方法；</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">//匹配名称中带有后缀Service的Bean。</div><div class="line">@Pointcut(&quot;bean(*Service)&quot;)</div><div class="line">private void myPointcut1()&#123;&#125;</div></pre></td></tr></table></figure></li><li><p>this ：用于匹配当前AOP代理对象类型的执行方法；请注意是AOP代理对象的类型匹配，这样就可能包括引入接口也类型匹配</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">//匹配了任意实现了UserDao接口的代理对象的方法进行过滤</div><div class="line">@Pointcut(&quot;this(com.gtw.spring.springAop.dao.UserDao)&quot;)</div><div class="line">private void myPointcut2()&#123;&#125;</div></pre></td></tr></table></figure></li><li><p>target ：用于匹配当前目标对象类型的执行方法；</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">//匹配了任意实现了UserDao接口的目标对象的方法进行过滤</div><div class="line">@Pointcut(&quot;target(com.gtw.spring.springAop.dao.UserDao)&quot;)</div><div class="line">private void myPointcut3()&#123;&#125;</div></pre></td></tr></table></figure></li><li><p>@within：用于匹配所以持有指定注解类型内的方法；请注意与within是有区别的， within是用于匹配指定类型内的方法执行；</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">//匹配使用了MarkerAnnotation注解的类(注意是类)</div><div class="line">@Pointcut(&quot;@within(com.gtw.spring.annotation.MarkerAnnotation)&quot;)</div><div class="line">private void myPointcut4()&#123;&#125;</div></pre></td></tr></table></figure></li><li><p>@annotation(com.gtw.spring.MarkerMethodAnnotation) : 根据所应用的注解进行方法过滤</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">//匹配使用了MarkerAnnotation注解的方法(注意是方法)</div><div class="line">@Pointcut(&quot;@annotation(com.gtw.spring.annotation.MarkerAnnotation)&quot;)</div><div class="line">private void myPointcut5()&#123;&#125;</div></pre></td></tr></table></figure></li></ul><p>ok~，关于表达式指示符就介绍到这，我们主要关心前面几个常用的即可，不常用过印象即可。这里最后说明一点，切点指示符可以使用运算符语法进行表达式的混编，如and、or、not（或者&amp;&amp;、||、！），如下一个简单例子：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">//匹配了任意实现了UserDao接口的目标对象的方法并且该接口不在com.gtw.dao包及其子包下</div><div class="line">@Pointcut(&quot;target(com.gtw.spring.springAop.dao.UserDao) ！within(com.gtw.dao..*)&quot;)</div><div class="line">private void myPointcut6()&#123;&#125;</div><div class="line">//匹配了任意实现了UserDao接口的目标对象的方法并且该方法名称为addUser</div><div class="line">@Pointcut(&quot;target(com.gtw.spring.springAop.dao.UserDao)&amp;&amp;execution(* com.gtw.spring.springAop.dao.UserDao.addUser(..))&quot;)</div><div class="line">private void myPointcut7()&#123;&#125;</div></pre></td></tr></table></figure><h2 id="通知函数以及传递参数"><a href="#通知函数以及传递参数" class="headerlink" title="通知函数以及传递参数"></a>通知函数以及传递参数</h2><h3 id="5种通知函数"><a href="#5种通知函数" class="headerlink" title="5种通知函数"></a>5种通知函数</h3><p>在Spring中与AspectJ一样，通知主要分5种类型，分别是前置通知、后置通知、异常通知、最终通知以及环绕通知，下面分别介绍。</p><ul><li><p>前置通知@Before</p><p>前置通知通过@Before注解进行标注，并可直接传入切点表达式的值，该通知在目标函数执行前执行，注意JoinPoint，是Spring提供的静态变量，<strong>通过joinPoint 参数，可以获取目标对象的信息,如类名称,方法参数,方法名称等，，该参数是可选的</strong>。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 前置通知</div><div class="line"> * <span class="doctag">@param</span> joinPoint 该参数可以获取目标对象的信息,如类名称,方法参数,方法名称等</div><div class="line"> */</div><div class="line"><span class="meta">@Before</span>(<span class="string">"execution(* com.gtw.spring.springAop.dao.UserDao.addUser(..))"</span>)</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">before</span><span class="params">(JoinPoint joinPoint)</span></span>&#123;</div><div class="line">    System.out.println(<span class="string">"我是前置通知"</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure><ul><li>后置通知@AfterReturning<br>通过@AfterReturning注解进行标注，该函数在目标函数执行完成后执行，并可以获取到目标函数最终的返回值returnVal，当目标函数没有返回值时，returnVal将返回null，必须通过returning = “returnVal”注明参数的名称而且必须与通知函数的参数名称相同。请注意，在任何通知中这些参数都是可选的，需要使用时直接填写即可，不需要使用时，可以完成不用声明出来。如下</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line">* 后置通知，不需要参数时可以不提供</div><div class="line">*/</div><div class="line"><span class="meta">@AfterReturning</span>(value=<span class="string">"execution(* com.gtw.spring.springAop.dao.UserDao.*User(..))"</span>)</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">AfterReturning</span><span class="params">()</span></span>&#123;</div><div class="line">   System.out.println(<span class="string">"我是后置通知..."</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line">* 后置通知</div><div class="line">* returnVal,切点方法执行后的返回值</div><div class="line">*/</div><div class="line"><span class="meta">@AfterReturning</span>(value=<span class="string">"execution(* com.gtw.spring.springAop.dao.UserDao.*User(..))"</span>,returning = <span class="string">"returnVal"</span>)</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">AfterReturning</span><span class="params">(JoinPoint joinPoint,Object returnVal)</span></span>&#123;</div><div class="line">   System.out.println(<span class="string">"我是后置通知...returnVal+"</span>+returnVal);</div><div class="line">&#125;</div></pre></td></tr></table></figure><ul><li>异常通知 @AfterThrowing</li></ul><p>该通知只有在异常时才会被触发，并由throwing来声明一个接收异常信息的变量，同样异常通知也用于Joinpoint参数，需要时加上即可，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line">* 抛出通知</div><div class="line">* <span class="doctag">@param</span> e 抛出异常的信息</div><div class="line">*/</div><div class="line"><span class="meta">@AfterThrowing</span>(value=<span class="string">"execution(* com.gtw.spring.springAop.dao.UserDao.addUser(..))"</span>,throwing = <span class="string">"e"</span>)</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">afterThrowable</span><span class="params">(Throwable e)</span></span>&#123;</div><div class="line">  System.out.println(<span class="string">"出现异常:msg="</span>+e.getMessage());</div><div class="line">&#125;</div></pre></td></tr></table></figure><ul><li>最终通知 @After</li></ul><p>该通知有点类似于finally代码块，只要应用了无论什么情况下都会执行。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 无论什么情况下都会执行的方法</div><div class="line"> * joinPoint 参数</div><div class="line"> */</div><div class="line"><span class="meta">@After</span>(<span class="string">"execution(* com.gtw.spring.springAop.dao.UserDao.*User(..))"</span>)</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">after</span><span class="params">(JoinPoint joinPoint)</span> </span>&#123;</div><div class="line">    System.out.println(<span class="string">"最终通知...."</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure><ul><li>环绕通知@Around<br>环绕通知既可以在目标方法前执行也可在目标方法之后执行，更重要的是环绕通知可以控制目标方法是否指向执行，但即使如此，我们应该尽量以最简单的方式满足需求，在仅需在目标方法前执行时，应该采用前置通知而非环绕通知。案例代码如下第一个参数必须是ProceedingJoinPoint，通过该对象的proceed()方法来执行目标函数，proceed()的返回值就是环绕通知的返回值。同样的，ProceedingJoinPoint对象也是可以获取目标对象的信息,如类名称,方法参数,方法名称等等。</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Around</span>(<span class="string">"execution(* com.gtw.spring.springAop.dao.UserDao.*User(..))"</span>)</div><div class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">around</span><span class="params">(ProceedingJoinPoint joinPoint)</span> <span class="keyword">throws</span> Throwable </span>&#123;</div><div class="line">    System.out.println(<span class="string">"我是环绕通知前...."</span>);</div><div class="line">    <span class="comment">//执行目标函数</span></div><div class="line">    Object obj= (Object) joinPoint.proceed();</div><div class="line">    System.out.println(<span class="string">"我是环绕通知后...."</span>);</div><div class="line">    <span class="keyword">return</span> obj;</div><div class="line">&#125;</div></pre></td></tr></table></figure><h3 id="通知传递参数"><a href="#通知传递参数" class="headerlink" title="通知传递参数"></a>通知传递参数</h3><p>在Spring AOP中，除了execution和bean指示符不能传递参数给通知方法，其他指示符都可以将匹配的方法相应参数或对象自动传递给通知方法。获取到匹配的方法参数后通过”argNames”属性指定参数名。如下，需要注意的是<strong>args(指示符)、argNames的参数名与before()方法中参数名 必须保持一致</strong>即param。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Before</span>(value=<span class="string">"args(param)"</span>, argNames=<span class="string">"param"</span>) <span class="comment">//明确指定了    </span></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">before</span><span class="params">(<span class="keyword">int</span> param)</span> </span>&#123;    </div><div class="line">    System.out.println(<span class="string">"param:"</span> + param);    </div><div class="line">&#125;</div></pre></td></tr></table></figure><p>当然也可以直接使用args指示符不带argNames声明参数，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Before</span>(<span class="string">"execution(public * com.gtw..*.addUser(..)) &amp;&amp; args(userId,..)"</span>)  </div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">before</span><span class="params">(<span class="keyword">int</span> userId)</span> </span>&#123;  </div><div class="line">    <span class="comment">//调用addUser的方法时如果与addUser的参数匹配则会传递进来会传递进来</span></div><div class="line">    System.out.println(<span class="string">"userId:"</span> + userId);  </div><div class="line">&#125;</div></pre></td></tr></table></figure><p>args(userId,..)该表达式会保证只匹配那些至少接收一个参数而且传入的类型必须与userId一致的方法，记住传递的参数可以简单类型或者对象，而且只有参数和目标方法也匹配时才有会有值传递进来。来个</p><h2 id="Aspect优先级"><a href="#Aspect优先级" class="headerlink" title="Aspect优先级"></a>Aspect优先级</h2><p>在不同的切面中，如果有多个通知需要在同一个切点函数指定的过滤目标方法上执行，那些在目标方法前执行(”进入”)的通知函数，最高优先级的通知将会先执行，在执行在目标方法后执行(“退出”)的通知函数，最高优先级会最后执行。而对于<strong>在同一个切面定义的通知函数将会根据在类中的声明顺序执行</strong>。如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Aspect</span></div><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AspectOne</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * Pointcut定义切点函数</div><div class="line">     */</div><div class="line">    <span class="meta">@Pointcut</span>(<span class="string">"execution(* com.gtw.spring.springAop.dao.UserDao.deleteUser(..))"</span>)</div><div class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">myPointcut</span><span class="params">()</span></span>&#123;&#125;</div><div class="line"></div><div class="line">    <span class="meta">@Before</span>(<span class="string">"myPointcut()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">beforeOne</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"前置通知....执行顺序1"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Before</span>(<span class="string">"myPointcut()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">beforeTwo</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"前置通知....执行顺序2"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@AfterReturning</span>(value = <span class="string">"myPointcut()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">AfterReturningThree</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"后置通知....执行顺序3"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@AfterReturning</span>(value = <span class="string">"myPointcut()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">AfterReturningFour</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"后置通知....执行顺序4"</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>在同一个切面中定义多个通知响应同一个切点函数，执行顺序为声明顺序：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">前置通知....执行顺序1</div><div class="line">前置通知....执行顺序2</div><div class="line">delete user</div><div class="line">后置通知....执行顺序4</div><div class="line">后置通知....执行顺序3</div></pre></td></tr></table></figure><p>如果在不同的切面中定义多个通知响应同一个切点，进入时则优先级高的切面类中的通知函数优先执行，退出时则最后执行，如下定义AspectOne类和AspectTwo类并实现org.springframework.core.Ordered 接口，该接口用于控制切面类的优先级，同时重写getOrder方法，定制返回值，返回值(int 类型)越小优先级越大。其中AspectOne返回值为0，AspectTwo的返回值为3，显然AspectOne优先级高于AspectTwo。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Aspect</span></div><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AspectOne</span> <span class="keyword">implements</span> <span class="title">Ordered</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * Pointcut定义切点函数</div><div class="line">     */</div><div class="line">    <span class="meta">@Pointcut</span>(<span class="string">"execution(* com.gtw.spring.springAop.dao.UserDao.deleteUser(..))"</span>)</div><div class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">myPointcut</span><span class="params">()</span></span>&#123;&#125;</div><div class="line"></div><div class="line">    <span class="meta">@Before</span>(<span class="string">"myPointcut()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">beforeOne</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"前置通知..AspectOne..执行顺序1"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Before</span>(<span class="string">"myPointcut()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">beforeTwo</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"前置通知..AspectOne..执行顺序2"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@AfterReturning</span>(value = <span class="string">"myPointcut()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">AfterReturningThree</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"后置通知..AspectOne..执行顺序3"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@AfterReturning</span>(value = <span class="string">"myPointcut()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">AfterReturningFour</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"后置通知..AspectOne..执行顺序4"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 定义优先级,值越低,优先级越高</div><div class="line">     * <span class="doctag">@return</span></div><div class="line">     */</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getOrder</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"></div><div class="line"><span class="comment">//切面类 AspectTwo.java</span></div><div class="line"><span class="meta">@Aspect</span></div><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AspectTwo</span> <span class="keyword">implements</span> <span class="title">Ordered</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * Pointcut定义切点函数</div><div class="line">     */</div><div class="line">    <span class="meta">@Pointcut</span>(<span class="string">"execution(* com.gtw.spring.springAop.dao.UserDao.deleteUser(..))"</span>)</div><div class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">myPointcut</span><span class="params">()</span></span>&#123;&#125;</div><div class="line"></div><div class="line">    <span class="meta">@Before</span>(<span class="string">"myPointcut()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">beforeOne</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"前置通知....执行顺序1--AspectTwo"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Before</span>(<span class="string">"myPointcut()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">beforeTwo</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"前置通知....执行顺序2--AspectTwo"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@AfterReturning</span>(value = <span class="string">"myPointcut()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">AfterReturningThree</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"后置通知....执行顺序3--AspectTwo"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@AfterReturning</span>(value = <span class="string">"myPointcut()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">AfterReturningFour</span><span class="params">()</span></span>&#123;</div><div class="line">        System.out.println(<span class="string">"后置通知....执行顺序4--AspectTwo"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 定义优先级,值越低,优先级越高</div><div class="line">     * <span class="doctag">@return</span></div><div class="line">     */</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getOrder</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> <span class="number">2</span>;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>运行结果如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">前置通知..AspectOne..执行顺序1</div><div class="line">前置通知..AspectOne..执行顺序2</div><div class="line">前置通知....执行顺序1--AspectTwo</div><div class="line">前置通知....执行顺序2--AspectTwo</div><div class="line">delete user</div><div class="line">后置通知....执行顺序4--AspectTwo</div><div class="line">后置通知....执行顺序3--AspectTwo</div><div class="line">后置通知..AspectOne..执行顺序4</div><div class="line">后置通知..AspectOne..执行顺序3</div></pre></td></tr></table></figure><p>案例中虽然只演示了前置通知和后置通知，但其他通知也遵循相同的规则，有兴趣可自行测试。到此基于注解的Spring AOP 分析就结束了。</p><h2 id="Spring-AOP-简单应用场景"><a href="#Spring-AOP-简单应用场景" class="headerlink" title="Spring AOP 简单应用场景"></a>Spring AOP 简单应用场景</h2><p>接下来的Spring AOP应用案例将会加深我们对于其的熟悉程度，这里将会模拟两种开发中常用的场景，注意这些场景的代码并不一定完全适合所有情况，但它们确实是可运用到实际软件开发中的，同时案例中的代码并不会以完整的项目演示而更倾向于模拟AOP的应用代码，那就由此开始吧。性能监测对于成熟的系统是不可或缺的，以下的代码段将程序消耗时间方面给我们带来直观的数据(消耗时间的大小)：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 计算消耗的时间</span></div><div class="line"><span class="keyword">long</span> start = System.currentTimeMillis();</div><div class="line"><span class="comment">//其他代码操作</span></div><div class="line"><span class="keyword">long</span> time = System.currentTimeMillis() - start;<span class="number">1234</span></div></pre></td></tr></table></figure><p>可预知这样的代码将系统中多次出现而且横跨各个模块，显然可以抽取横切关注点，使用AOP来完成，下面的代码将会模拟监测api接口访问的性能并记录每个接口的名称包含类名以及消耗时间等，简单定义一个监控信息记录类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MonitorTime</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="keyword">private</span> String className;</div><div class="line">    <span class="keyword">private</span> String methodName;</div><div class="line">    <span class="keyword">private</span> Date logTime;</div><div class="line">    <span class="keyword">private</span> <span class="keyword">long</span> comsumeTime;</div><div class="line">    <span class="comment">//......其他属性</span></div><div class="line"></div><div class="line">    <span class="comment">//省略set 和 get</span></div><div class="line"></div><div class="line"> &#125;</div></pre></td></tr></table></figure><p>定义一个监控的切面类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Aspect</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TimerAspect</span> </span>&#123;</div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 定义切点函数，过滤controller包下的名称以Controller结尾的类所有方法</div><div class="line">     */</div><div class="line">    <span class="meta">@Pointcut</span>(<span class="string">"execution(* com.gtw.spring.controller.*Controller.*(..))"</span>)</div><div class="line">    <span class="function"><span class="keyword">void</span> <span class="title">timer</span><span class="params">()</span> </span>&#123;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Around</span>(<span class="string">"timer()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">logTimer</span><span class="params">(ProceedingJoinPoint thisJoinPoint)</span> <span class="keyword">throws</span> Throwable </span>&#123;</div><div class="line"></div><div class="line">        MonitorTime monitorTime=<span class="keyword">new</span> MonitorTime();</div><div class="line">        <span class="comment">//获取目标类名称</span></div><div class="line">        String clazzName = thisJoinPoint.getTarget().getClass().getName();</div><div class="line">        <span class="comment">//获取目标类方法名称</span></div><div class="line">        String methodName = thisJoinPoint.getSignature().getName();</div><div class="line"></div><div class="line">        monitorTime.setClassName(clazzName);<span class="comment">//记录类名称</span></div><div class="line">        monitorTime.setMethodName(methodName);<span class="comment">//记录对应方法名称</span></div><div class="line">        monitorTime.setLogTime(<span class="keyword">new</span> Date());<span class="comment">//记录时间</span></div><div class="line"></div><div class="line">        <span class="comment">// 计时并调用目标函数</span></div><div class="line">        <span class="keyword">long</span> start = System.currentTimeMillis();</div><div class="line">        Object result = thisJoinPoint.proceed();</div><div class="line">        <span class="keyword">long</span> time = System.currentTimeMillis() - start;</div><div class="line"></div><div class="line">        <span class="comment">//设置消耗时间</span></div><div class="line">        monitorTime.setComsumeTime(time);</div><div class="line">        <span class="comment">//把monitorTime记录的信息上传给监控系统，并没有实现，需要自行实现即可</span></div><div class="line">        <span class="comment">//MonitoruUtils.report(monitorTime)</span></div><div class="line"></div><div class="line">        <span class="keyword">return</span> result;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>在每次访问切点函数定义过滤的方法时就计算出每个接口消耗的时间，以此来作为访问接口的性能指标。除了性能监控，可能还有比这更重要的异常监控，而异常的处理在各个模块中都是不可避免的，为了避免到处编写try/catch，统一处理异常是个非常不错的主意，Spring AOP 显然可以胜任 ，我们完全可以在切面类中使用环绕通知统一处理异常信息，并把异常信息封装上报给后台日志系统，以下的代码将体现出这一机智的操作。首先编写一个异常封装类(仅简单模拟)：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 封装异常信息的类</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ExceptionInfo</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="keyword">private</span> String className;</div><div class="line">    <span class="keyword">private</span> String methodName;</div><div class="line">    <span class="keyword">private</span> Date logTime;<span class="comment">//异常记录时间</span></div><div class="line">    <span class="keyword">private</span> String message;<span class="comment">//异常信息</span></div><div class="line">    <span class="comment">//.....其他属性</span></div><div class="line">    <span class="comment">//省略set get</span></div><div class="line">&#125;</div></pre></td></tr></table></figure><p>统一处理异常的切面类如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 异常监测切面类</div><div class="line"> */</div><div class="line"><span class="meta">@Aspect</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ExceptionMonitor</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 定义异常监控类</div><div class="line">     */</div><div class="line">    <span class="meta">@Pointcut</span>(<span class="string">"execution(com.gtw.spring *(..))"</span>)</div><div class="line">    <span class="function"><span class="keyword">void</span> <span class="title">exceptionMethod</span><span class="params">()</span> </span>&#123;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Around</span>(<span class="string">"exceptionMethod()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">monitorMethods</span><span class="params">(ProceedingJoinPoint thisJoinPoint)</span> </span>&#123;</div><div class="line">        <span class="keyword">try</span> &#123;</div><div class="line">            <span class="keyword">return</span> thisJoinPoint.proceed();</div><div class="line">        &#125; <span class="keyword">catch</span> (Throwable e) &#123;</div><div class="line">            ExceptionInfo info=<span class="keyword">new</span> ExceptionInfo();</div><div class="line">            <span class="comment">//异常类记录</span></div><div class="line">            info.setClassName(thisJoinPoint.getTarget().getClass().getName());</div><div class="line">            info.setMethodName(thisJoinPoint.getSignature().getName());</div><div class="line">            info.setLogTime(<span class="keyword">new</span> Date());</div><div class="line">            info.setMessage(e.toString());</div><div class="line">            <span class="comment">//上传日志系统,自行完善</span></div><div class="line">            <span class="comment">//ExceptionReportUtils.report(info);</span></div><div class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>关于异常监控ExceptionMonitor中的通知函数体，这里仅简单演示代码实现，实际开发中根据业务需求修改即可。AOP的应用远不止这两种，诸如缓存，权限验证、内容处理、事务控制等都可以使用AOP实现，其中事务控制Spring中提供了专门的处理方式。</p><p><a href="https://github.com/gaotingwang/skill-util/tree/master/proxy-pattern" target="_blank" rel="external">【GitHub JDK/CGLIB Proxy源码下载】</a></p><p><a href="https://github.com/gaotingwang/skill-util/tree/master/aop-transactional" target="_blank" rel="external">【GitHub Spring AOP 源码下载】</a></p></div><div><div><div style="text-align:center;margin-top:35px;color:#ccc;font-size:14px">------------------------------------本文结束<i class="fa fa-paw"></i>感谢阅读------------------------------------</div></div></div><div><div style="padding:10px 0;margin:20px auto;width:90%;text-align:center"><div>坚持技术分享，你的支持将鼓励我继续创作！</div> <button id="rewardButton" disable="enable" onclick='var qr=document.getElementById("QR");"none"===qr.style.display?qr.style.display="block":qr.style.display="none"'> <span>打赏</span></button><div id="QR" style="display:none"><div id="wechat" style="display:inline-block"> <img id="wechat_qr" src="http://gtw.oss-cn-shanghai.aliyuncs.com/reward.jpg" alt="脱毛大白兔 微信支付"><p>微信支付</p></div></div></div></div><footer class="post-footer"><div class="post-nav"><div class="post-nav-next post-nav-item"><a href="/2017/02/25/Spring IOC/" rel="next" title="Spring IOC 原理概述"><i class="fa fa-chevron-left"></i> Spring IOC 原理概述</a></div><span class="post-nav-divider"></span><div class="post-nav-prev post-nav-item"> <a href="/2017/03/01/Spring AOP/" rel="prev" title="Spring AOP 实现原理">Spring AOP 实现原理<i class="fa fa-chevron-right"></i></a></div></div></footer></div></article><div class="post-spread"></div></div></div><div class="comments" id="comments"><div id="gitment-container"></div></div></div><div class="sidebar-toggle"><div class="sidebar-toggle-line-wrap"><span class="sidebar-toggle-line sidebar-toggle-line-first"></span><span class="sidebar-toggle-line sidebar-toggle-line-middle"></span><span class="sidebar-toggle-line sidebar-toggle-line-last"></span></div></div><aside id="sidebar" class="sidebar"><div class="sidebar-inner"><ul class="sidebar-nav motion-element"><li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap"> 文章目录</li><li class="sidebar-nav-overview" data-target="site-overview-wrap"> 站点概览</li></ul><section class="site-overview-wrap sidebar-panel"><div class="site-overview"><div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person"> <img class="site-author-image" itemprop="image" src="http://gtw.oss-cn-shanghai.aliyuncs.com/myself/my3.jpeg?x-oss-process=style/zoom" alt="脱毛大白兔"><p class="site-author-name" itemprop="name">脱毛大白兔</p><p class="site-description motion-element" itemprop="description">一路哼哼唧唧，潇潇洒洒，飘飘扬扬地走下去...</p></div><nav class="site-state motion-element"><div class="site-state-item site-state-posts"> <a href="/archives/"><span class="site-state-item-count">51</span> <span class="site-state-item-name">日志</span></a></div><div class="site-state-item site-state-categories"> <a href="/categories/index.html"><span class="site-state-item-count">9</span> <span class="site-state-item-name">分类</span></a></div><div class="site-state-item site-state-tags"> <a href="/tags/index.html"><span class="site-state-item-count">10</span> <span class="site-state-item-name">标签</span></a></div></nav><div id="music163player" style="margin-bottom:-25px"><iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width="2800" height="86" src="//music.163.com/outchain/player?type=2&id=2408566&auto=0&height=66"></iframe></div><div class="links-of-author motion-element"><span class="links-of-author-item"><a href="https://github.com/gaotingwang" target="_blank" title="GitHub"><i class="fa fa-fw fa-github"></i> GitHub</a></span><span class="links-of-author-item"><a href="mailto:code_butcher@sina.com" target="_blank" title="E-Mail"><i class="fa fa-fw fa-envelope"></i> E-Mail</a></span></div><div class="links-of-blogroll motion-element links-of-blogroll-block"><div class="links-of-blogroll-title"><i class="fa fa-fw fa-link"></i> 友情链接</div><ul class="links-of-blogroll-list"><li class="links-of-blogroll-item"> <a href="http://www.ruanyifeng.com/blog/" title="阮一峰日志" target="_blank">阮一峰日志</a></li><li class="links-of-blogroll-item"> <a href="http://blog.didispace.com/" title="程序员DD博客" target="_blank">程序员DD博客</a></li></ul></div></div></section><section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active"><div class="post-toc"><div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#Spring-AOP的实现原理概要"><span class="nav-number">1.</span> <span class="nav-text">Spring AOP的实现原理概要</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#JDK动态代理"><span class="nav-number">1.1.</span> <span class="nav-text">JDK动态代理</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#CGLIB动态代理"><span class="nav-number">1.2.</span> <span class="nav-text">CGLIB动态代理</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#基于Aspect-Spring-AOP-开发"><span class="nav-number">2.</span> <span class="nav-text">基于Aspect Spring AOP 开发</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#简单案例快速入门"><span class="nav-number">2.1.</span> <span class="nav-text">简单案例快速入门</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#再谈Spring-AOP-术语"><span class="nav-number">2.2.</span> <span class="nav-text">再谈Spring AOP 术语</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#基于注解的Spring-AOP开发"><span class="nav-number">3.</span> <span class="nav-text">基于注解的Spring AOP开发</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#定义切入点函数"><span class="nav-number">3.1.</span> <span class="nav-text">定义切入点函数</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#切入点指示符"><span class="nav-number">3.2.</span> <span class="nav-text">切入点指示符</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#通配符"><span class="nav-number">3.2.1.</span> <span class="nav-text">通配符</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#类型签名表达式"><span class="nav-number">3.2.2.</span> <span class="nav-text">类型签名表达式</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#方法签名表达式"><span class="nav-number">3.2.3.</span> <span class="nav-text">方法签名表达式</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#其他指示符"><span class="nav-number">3.2.4.</span> <span class="nav-text">其他指示符</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#通知函数以及传递参数"><span class="nav-number">3.3.</span> <span class="nav-text">通知函数以及传递参数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#5种通知函数"><span class="nav-number">3.3.1.</span> <span class="nav-text">5种通知函数</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#通知传递参数"><span class="nav-number">3.3.2.</span> <span class="nav-text">通知传递参数</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Aspect优先级"><span class="nav-number">3.4.</span> <span class="nav-text">Aspect优先级</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Spring-AOP-简单应用场景"><span class="nav-number">3.5.</span> <span class="nav-text">Spring AOP 简单应用场景</span></a></li></ol></li></ol></div></div></section></div></aside></div></main><footer id="footer" class="footer"><div class="footer-inner"><div class="copyright">&copy; 2015 &mdash; <span itemprop="copyrightYear">2018</span><span class="with-love"><i class="fa fa-user"></i></span> <span class="author" itemprop="copyrightHolder">脱毛大白兔</span></div></div></footer><div class="back-to-top"><i class="fa fa-arrow-up"></i> <span id="scrollpercent"><span>0</span>%</span></div></div><script type="text/javascript">"[object Function]"!==Object.prototype.toString.call(window.Promise)&&(window.Promise=null)</script><script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script><script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script><script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script><script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script><script type="text/javascript" src="/lib/three/three.min.js"></script><script type="text/javascript" src="/lib/three/three-waves.min.js"></script><script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script><link rel="stylesheet" href="https://aimingoo.github.io/gitmint/style/default.css"><script src="https://aimingoo.github.io/gitmint/dist/gitmint.browser.js"></script><style>a.gitment-editor-footer-tip{display:none}.gitment-container.gitment-footer-container{display:none}</style><script type="text/javascript">function renderGitment(){new Gitmint({id:window.location.pathname,owner:"gaotingwang",repo:"blog-comments",lang:"en",oauth:{client_secret:"9e283c98b95c605df85e3c7ab94459861b670c6d",client_id:"0c1815a490b171e0c853"}}).render("gitment-container")}renderGitment()</script><link rel="stylesheet" href="/lib/algolia-instant-search/instantsearch.min.css"><script src="/lib/algolia-instant-search/instantsearch.min.js"></script><script src="/js/src/algolia-search.js?v=5.1.4"></script><script src="https://cdn1.lncld.net/static/js/av-core-mini-0.6.4.js"></script><script>AV.initialize("d1eLP5bbeDvg09d922elyLBr-gzGzoHsz","MCTjBqQOcTRrfvljPi77Fzsm")</script><script>function showTime(e){var t=new AV.Query(e),n=[],o=$(".leancloud_visitors");o.each(function(){n.push($(this).attr("id").trim())}),t.containedIn("url",n),t.find().done(function(e){var t=".leancloud-visitors-count";if(0!==e.length){for(var i=0;i<e.length;i++){var s=e[i],r=s.get("url"),l=s.get("time"),c=document.getElementById(r);$(c).find(t).text(l)}for(i=0;i<n.length;i++){r=n[i],c=document.getElementById(r);var u=$(c).find(t);""==u.text()&&u.text(0)}}else o.find(t).text(0)}).fail(function(e,t){console.log("Error: "+t.code+" "+t.message)})}function addCount(e){var t=$(".leancloud_visitors"),n=t.attr("id").trim(),o=t.attr("data-flag-title").trim(),i=new AV.Query(e);i.equalTo("url",n),i.find({success:function(t){if(t.length>0){var i=t[0];i.fetchWhenSave(!0),i.increment("time"),i.save(null,{success:function(e){$(document.getElementById(n)).find(".leancloud-visitors-count").text(e.get("time"))},error:function(e,t){console.log("Failed to save Visitor num, with error message: "+t.message)}})}else{var s=new e,r=new AV.ACL;r.setPublicReadAccess(!0),r.setPublicWriteAccess(!0),s.setACL(r),s.set("title",o),s.set("url",n),s.set("time",1),s.save(null,{success:function(e){$(document.getElementById(n)).find(".leancloud-visitors-count").text(e.get("time"))},error:function(e,t){console.log("Failed to create")}})}},error:function(e){console.log("Error:"+e.code+" "+e.message)}})}$(function(){var e=AV.Object.extend("Counter");1==$(".leancloud_visitors").length?addCount(e):$(".post-title-link").length>1&&showTime(e)})</script><script>!function(){var t=document.createElement("script"),e=window.location.protocol.split(":")[0];t.src="https"===e?"https://zz.bdstatic.com/linksubmit/push.js":"http://push.zhanzhang.baidu.com/push.js";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}()</script><script type="text/x-mathjax-config">
  MathJax.Hub.Config("MathJax config");
</script><script type="text/x-mathjax-config">
  MathJax.Hub.Queue(function() {
    var all = MathJax.Hub.getAllJax(), i;
    for(i=0; i < all.length; i += 1) {
      all[i].SourceElement().parentNode.className += ' has-jax';
    }
  });
</script><script type="text/javascript" src="custom_mathjax_source"></script></body></html>